'use strict';
let value_ns = new function() {
	let Value = this.Value = class extends EventEmitter {
		constructor(value = null, p = {}) {
			super();
			this.locked = p.locked ?? false;
			this.auto_update = p.auto_update ?? true;
			this.auto_update_oninit = p.auto_update_oninit ?? true;
			
			this._getter = this._setter = this.evaluate = this._cleanup = () => void 0;
			
			Value._init_.call(this, value);
		}
		
		get type() { return this._type; }
		
		set value(v) { this._setter(v); }
		get value() { return this._getter(); }
		
		new(v) {
			this._cleanup();
			this._cleanup = () => void 0;
			Value._init_.call(this, v);
		}
		
		lock(a) { this.locked = a !== undefined ? a : !this.locked; }
		
		valueOf() { return this.value; }
		toString() { return this.value; }
		[Symbol.toPrimitive](type) { return this.value; }
		
		
		static getTypeOf(v) {
			for(let i in this.types) { if(this.types[i](v)) return i; };
			return 'none';
		}
		
		static _init_(value) {
			this._type = Value.getTypeOf(value);
			
			if(this.type === 'none') throw Error('value is none');
			Value.init_types[this.type].call(this, value);
		}
		
		static types = {};
		static init_types = {};
		
		static createType(type, isType, _init_) {
			this.types[type] = isType;
			this.init_types[type] = _init_;
		}
	};
	Value.prototype[Symbol.toStringTag] = 'Value';
	
	
	Value.createType('primitive', v => typeof v === 'string' || typeof v === 'number', function(value) {
		this._getter = () => this._value;
		this._setter = v => this._value = v;
		
		this.value = value;
	});
	
	
	Value.createType('reference', v => v instanceof Value, function(value) {
		this.evaluate = () => this._value = value.value;
		
		this._getter = () => {
			if(this.auto_update) this.evaluate();
			return this._value;
		};
		
		this._setter = v => { if(!this.locked) value.value = v; };
		
		if(this.auto_update_oninit) this.evaluate();
	});
	
	
	Value.createType('expression', v => typeof v === 'function', function(value) {
		let self = this;
		
		let api = {
			set value(v) { self._value = v; },
			get value() { return self._value; }
		};
		
		this.evaluate = (...args) => this._value = value.apply(api, args) ?? this._value;
		
		this._getter = () => {
			if(this.auto_update) this.evaluate();
			return this._value;
		};
		this._setter = v => this.evaluate(v);
		
		if(this.auto_update_oninit) this.evaluate();
	});
	
	
	Value.createType('Vector2', v => v instanceof Vector2, function(value) {
		this._getter = () => this._value;
		this._setter = v => this._value.set(v);
		
		this._value = value;
		
		
		let prevProto1 = value.__proto__.__proto__;
		value.__proto__.__proto__= this.__proto__;
		
		let prevProto2 = this.__proto__;
		this.__proto__ = value;
		
		this._cleanup = () => {
			value.__proto__.__proto__ = prevProto1;
			this.__proto__ = prevProto2
		};
	});
};
